package com.example.demo.base.login.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.demo.base.login.dto.*;
import com.example.demo.base.login.service.ExamUserService;
import com.example.demo.base.login.service.RsaService;
import com.example.demo.common.BusinessStateCode;
import com.example.demo.constants.RedisKey;
import com.example.demo.domain.entity.*;
import com.example.demo.dto.ResponseDto;
import com.example.demo.dto.RolePermissionDto;
import com.example.demo.enums.CompanyEnum;
import com.example.demo.enums.UserIdentifyEnum;
import com.example.demo.enums.UserStatusEnum;
import com.example.demo.exception.ExceptionUtils;
import com.example.demo.exception.ValidatorException;
import com.example.demo.mapper.*;
import com.example.demo.utils.*;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;



@Service
@Transactional
public class ExamUserServiceImpl implements ExamUserService {

    private static final String Rsa_Redis_Public_Key = "Rsa_Redis_Public_Key";

    private static final String Rsa_Redis_Private_Key = "Rsa_Redis_Private_Key";

    private static final String LOGIN_STATUS_PWD = "1";

    private static final String LOGIN_STATUS_PHONE = "2";

    private static final String LOGIN_STATUS_EMAIL = "3";

    private static final String LOGIN_TYPE_USER = "1";

    private static final String LOGIN_TYPE_ADMIN = "2";

    private static final String defaultPwd = "12345";

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private UserMapper userMapper;

    @Resource
    private RolePermissionMapper rolePermissionMapper;

    @Resource
    private PermissionFrontUrlMapper permissionFrontUrlMapper;

    @Resource
    private MessageUtil messageUtil;

    @Resource
    private RsaService rsaService;


    @Value("${login.expire}")
    private Long LOGIN_REDIS_EXPIRE_TIME;

    @Value("${login.phoneCode.expire}")
    private Long LOGIN_REDIS_PHONE_EXPIRE_TIME;

    @Value("${register.user.role}")
    private String REGISTER_USER_ROLE;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private CompanyMapper companyMapper;

//    @Value("${redis.expireTime}")
//    Long expireTime;

    @Autowired
    Gson gson;

    @Override
    public LoginWebVo userLogin(LoginVo loginVo, HttpServletRequest request) {
        LoginWebVo loginWebVo = new LoginWebVo();
        validLoginVo(loginVo);


        UserCache userCache = new UserCache();
        // 如果用 账号密码登入
        if (LOGIN_STATUS_PWD.equals(loginVo.getLoginStatus())) {
            ValidatorUtil.require(loginVo.getUsername(), "账号");
            ValidatorUtil.require(loginVo.getPassword(), "密码");
            String userName = rsaService.RSADecrypt(loginVo.getUsername(),loginVo.getRandom());
            String pwd = Md5Utils.encode(rsaService.RSADecrypt(loginVo.getPassword(),loginVo.getRandom()));

            QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
            queryWrapper.eq("username", userName);
            queryWrapper.eq("password", pwd);
            User user = userMapper.selectOne(queryWrapper);
            if (null != user) {
                if (UserStatusEnum.forbid.getIdentify().equals(user.getStatus())) {
                    throw new ValidatorException("账号已经被禁用，具体原因请联系管理员");
                }
            } else {
                throw new ValidatorException("账号密码错误");
            }

            if (LOGIN_TYPE_USER.equals(loginVo.getType())) {    // 用户登入
                loginByUserNameAndPwd(user,userCache,loginWebVo,request);
            } else if (LOGIN_TYPE_ADMIN.equals(loginVo.getType())) {  // 管理员登入
                vaildCompany(user.getRole());  //验证
                if (UserStatusEnum.firstLogin.getIdentify().equals(user.getStatus())){  // 如果初次登入
                    loginWebVo.setStatus(user.getStatus());
                    return loginWebVo;
                }
                loginByUserNameAndPwd(user,userCache,loginWebVo,request);
            }
        }   // 手机号账号密码登入
        else if (LOGIN_STATUS_PHONE.equals(loginVo.getLoginStatus())){
            ValidatorUtil.require(loginVo.getPhone(), "手机号");
            ValidatorUtil.require(loginVo.getPassword(), "密码");
            String phone = rsaService.RSADecrypt(loginVo.getPhone(),loginVo.getRandom());
            String pwd = Md5Utils.encode(rsaService.RSADecrypt(loginVo.getPassword(),loginVo.getRandom()));

            QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
            queryWrapper.eq("phone", phone);
            queryWrapper.eq("password", pwd);
            User user = userMapper.selectOne(queryWrapper);
            if (null != user) {
                if (UserStatusEnum.forbid.getIdentify().equals(user.getStatus())) {
                    throw new ValidatorException("账号已经被禁用，具体原因请联系管理员");
                }
            } else {
                throw new ValidatorException("手机号密码错误");
            }

            if (LOGIN_TYPE_USER.equals(loginVo.getType())) {
                loginByUserNameAndPwd(user,userCache,loginWebVo,request);
            } else if (LOGIN_TYPE_ADMIN.equals(loginVo.getType())) {
                vaildCompany(user.getRole());
                if (UserStatusEnum.firstLogin.getIdentify().equals(user.getStatus())){
                    loginWebVo.setStatus(user.getStatus());
                    return loginWebVo;
                }
                loginByUserNameAndPwd(user,userCache,loginWebVo,request);
            }
        }
        return loginWebVo;
    }


    private void vaildCompany(String role){  // 拦截器
        if(StringUtils.isEmpty(role)){
            throw new ValidatorException("账号未分配角色");
        }

        UserRole userRole = userRoleMapper.selectById(Long.valueOf(role));

        if (null == userRole){
            throw new ValidatorException("未查询到角色信息");
        }

        Company company = companyMapper.selectById(userRole.getCId());

        if (null == company){
            throw new ValidatorException("未查询到公司信息");
        }

        if (CompanyEnum.status_two.getValue().equals(company.getStatus())){
            throw new ValidatorException("公司已经被禁用");
        }

    }


    /**
     * 账户密码登录方式
     *
     */

    private void loginByUserNameAndPwd(User user,UserCache userCache,LoginWebVo loginWebVo,HttpServletRequest request){

        if (!UserIdentifyEnum.superAdmin.getIdentify().equals(user.getIdentify())){  // 如果不是超级管理员登入
            // 校验系统是否开启
            if (redisUtil.existKey(RedisKey.SYS_SWITCH)){          // 系统关闭了则只有超级管理员才能登入
                String value = redisUtil.getValue(RedisKey.SYS_SWITCH);
                if ("0".equals(value)){
                    throw new ValidatorException("系统暂停使用");
                }
            }else {
                throw new ValidatorException("系统暂停使用");
            }
        }
        // redis里面存auth的key 存在则删除 旧key。用于实现每次登入都有新key？
        if (redisUtil.existKey(TokenUtils.getAdminRedisKey(user.getUsername(), user.getPassword()))) {
            String oldToken = redisUtil.getValue(TokenUtils.getAdminRedisKey(user.getUsername(), user.getPassword()));
            redisUtil.delKey(oldToken);
        }

        //每个用户对应的动态Token，每次登陆Token都会变
        String newToken = TokenUtils.createToken(user.getUsername(), user.getPassword());
        redisUtil.setKeyAndExpire(TokenUtils.getAdminRedisKey(user.getUsername(), user.getPassword()),
                newToken,
                LOGIN_REDIS_EXPIRE_TIME);
        // redisUtil.setKey(TokenUtils.getAdminRedisKey(user.getUsername(), user.getPassword()),
        //         newToken
        //         // ,LOGIN_REDIS_EXPIRE_TIME
        // );
        String[] split = user.getRole().split(",");
        // TODO: 2023/11/5 你们还需要什么写在这里
        // userCache 用于存取用户的具体信息
        userCache.setUser(user);
        userCache.setIp(GetIpAddrUtil.getIpAddr(request));
        // 缓存权限处理

        // 存取用户信息
        if (!StringUtils.isEmpty(user.getRole())) {  // 如果该用户被分配了角色
            String roleId = user.getRole().split(",")[0];
            Map roleMap = redisUtil.getMap(RedisKey.ROLE_COMPANY);
            Long companyId = Long.valueOf(roleMap.get(Integer.valueOf(roleId)).toString());
            userCache.setCompanyId(companyId);
            Map nameMap = redisUtil.getMap(RedisKey.COMPANY_ID_NAME);
            String companyName = nameMap.get(Integer.valueOf(companyId.toString())).toString();
            userCache.setCompanyName(companyName);
        }
        //token存用户具体信息
        redisUtil.setKeyAndExpire(newToken, gson.toJson(userCache), LOGIN_REDIS_EXPIRE_TIME);

        //权限处理
        List<RolePermissionDto> rolePermissionDtos = new ArrayList<>();

        List<Long> fids = new ArrayList<>();
        // spilt 是数组将 user.getRole 分割开来存
        if (split.length>0){   //????? 应该是添加权限
            for (String s : split) {  // QueryWrapper 是mybatis plus中实现查询的对象封装操作类
                QueryWrapper<RolePermission> queryWrapper = new QueryWrapper<RolePermission>();
                queryWrapper.eq("role_id", Integer.valueOf(s));
                List<RolePermission> rolePermissions = rolePermissionMapper.selectList(queryWrapper);
                for (RolePermission rolePermission : rolePermissions) {
                    if (!fids.contains(rolePermission.getfId())){
                        fids.add(rolePermission.getfId());
                    }
                }
            }
        }

        if (fids.size()>0){
            List<PermissionFrontUrl> permissionFrontUrls = permissionFrontUrlMapper.selectBatchIds(fids);
            for (PermissionFrontUrl permissionFrontUrl : permissionFrontUrls) {
                RolePermissionDto rolePermissionDto = new RolePermissionDto();
                rolePermissionDto.setFrontUrl(permissionFrontUrl.getFrontUrl());
                rolePermissionDto.setFrontName(permissionFrontUrl.getFrontName());
                rolePermissionDto.setParentId(permissionFrontUrl.getParentId());
                rolePermissionDtos.add(rolePermissionDto);
            }
        }

        loginWebVo.setRolePermissionDtos(rolePermissionDtos);
//                    //存用户对应的多个角色id
//                    redisUtil.set(tokenUtils.getAdminRedisAuthKey(loginVo.getAccount(), loginVo.getPwd()),
//                            gson.toJson(examUserMapper.findAdminRole(loginVo.getAccount())));

        loginWebVo.setToken(newToken);
        loginWebVo.setLoginstatus(true);
        loginWebVo.setStatus(user.getStatus());
        User userModify = new User();

        QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
        queryWrapper.eq("id", user.getId());

        userModify.setLastLoginIp(GetIpAddrUtil.getIpAddr(request));
        userModify.setLastLoginTime(System.currentTimeMillis());
        userMapper.update(userModify, queryWrapper);
    }

    private void validLoginVo(LoginVo loginVo) {
        ValidatorUtil.require(loginVo.getType(), "登陆身份");
        ValidatorUtil.require(loginVo.getLoginStatus(), "登陆方式");
//        ValidatorUtil.require(loginVo.getUsername(), "账号");
//        ValidatorUtil.require(loginVo.getPassword(), "密码");
    }

    @Override
    public RsaPublicKeyBo getPublicKey() {
        return rsaService.generateKey();
    }

    @Override
    public void modifyPwd(LoginVo loginVo) {
        validLoginVo(loginVo);
        ValidatorUtil.require(loginVo.getUsername(), "账号");
        ValidatorUtil.require(loginVo.getPassword(), "密码");
        String userName = rsaService.RSADecrypt(loginVo.getUsername(),loginVo.getRandom());
        String pwd = Md5Utils.encode(rsaService.RSADecrypt(loginVo.getPassword(),loginVo.getRandom()));

        User user = getUserByUserName(userName);
        User userModify = new User();
        userModify.setPassword(pwd);
        userModify.setStatus(UserStatusEnum.normal.getIdentify());

        QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
        queryWrapper.eq("id", user.getId());

        userMapper.update(userModify, queryWrapper);

    }

    @Override
    public void getPhoneCode(LoginVo loginVo) {  // 获取手机验证码
        // 校验参数
        ValidatorUtil.require(loginVo.getUsername(), "账号");
        ValidatorUtil.require(loginVo.getPhone(), "手机号");


        String userName = rsaService.RSADecrypt(loginVo.getUsername(),loginVo.getRandom());
        String phone = rsaService.RSADecrypt(loginVo.getPhone(),loginVo.getRandom());

        if (redisUtil.existKey(TokenUtils.getRedisKeyPhone(userName.concat(":").concat(phone)))){
            throw new ValidatorException("手机号:".concat(phone).concat("验证码未过期，请稍后重试"));
        }

        String phoneCode = TokenUtils.getRedisKeyPhone(userName.concat(":").concat(phone));

        String sixPhoneCode = MessageUtil.getSixPhoneCode();

        redisUtil.setKeyAndExpire(phoneCode,
                sixPhoneCode,
                LOGIN_REDIS_PHONE_EXPIRE_TIME);

        try {
            messageUtil.sendMessage(phone,sixPhoneCode);
        } catch (Exception e){
            throw new ValidatorException("手机号:".concat(phone).concat("发送短信失败，请稍后重试"));
        }

    }

    @Override
    public AdminRegisterWebVo adminRegister(AdminRegisterVo adminRegisterVo, HttpServletRequest request) {
        // 参数校验
        ValidatorUtil.require(adminRegisterVo.getUsername(), "账号");
        ValidatorUtil.require(adminRegisterVo.getIdentify(), "身份");
        ValidatorUtil.require(adminRegisterVo.getPhone(), "手机号");

        // 加密
        String userName = rsaService.RSADecrypt(adminRegisterVo.getUsername(),adminRegisterVo.getRandom());
        String phone = rsaService.RSADecrypt(adminRegisterVo.getPhone(),adminRegisterVo.getRandom());
        userNameUnique(userName);
        String pwd = Md5Utils.encode(defaultPwd);
        String role = rsaService.RSADecrypt(adminRegisterVo.getRole(),adminRegisterVo.getRandom());

        phoneUnique(phone);

        User user = new User();
        user.setUsername(userName);
        user.setRole(role);
        user.setPassword(pwd);
        user.setIdentify(adminRegisterVo.getIdentify());
        user.setStatus(UserStatusEnum.firstLogin.getIdentify());
        user.setIsIdentity(0);
        user.setPhone(phone);
        userMapper.insert(user);
        return null;
    }

    @Override
    public AdminRegisterWebVo userRegister(AdminRegisterVo adminRegisterVo, HttpServletRequest request) {
        ValidatorUtil.require(adminRegisterVo.getUsername(), "账号");
        ValidatorUtil.require(adminRegisterVo.getPassword(), "密码");
        ValidatorUtil.require(adminRegisterVo.getCode(), "验证码");
        ValidatorUtil.require(adminRegisterVo.getPhone(), "手机号");



        // 先验证手机验证码
        String userName = rsaService.RSADecrypt(adminRegisterVo.getUsername(),adminRegisterVo.getRandom());
        String phone = rsaService.RSADecrypt(adminRegisterVo.getPhone(),adminRegisterVo.getRandom());
        String code = rsaService.RSADecrypt(adminRegisterVo.getCode(),adminRegisterVo.getRandom());
        String pwd = rsaService.RSADecrypt(adminRegisterVo.getPassword(),adminRegisterVo.getRandom());

        String pwdTemp = Md5Utils.encode(pwd);

        userNameUnique(userName);
        phoneUnique(phone);


        phoneValid(userName,phone,code);

        User user = new User();
        user.setUsername(userName);

        // 生成随机昵称，用于展示排行榜
        String randomNickName = "用户"+System.currentTimeMillis();
        user.setNickName(randomNickName);
        // todo
        user.setRole(REGISTER_USER_ROLE);
        user.setPassword(pwdTemp);
        user.setIdentify(UserIdentifyEnum.normalUser.getIdentify());
        user.setStatus(UserStatusEnum.normal.getIdentify());
        user.setPhone(phone);
        user.setIsIdentity(0);
        userMapper.insert(user);
        return null;
    }

    @Override
    public Void adminAcativity(AdminAcativityVo adminAcativityVo) {
        ValidatorUtil.require(adminAcativityVo.getUsername(), "账号");
        ValidatorUtil.require(adminAcativityVo.getPassword(), "密码");
        ValidatorUtil.require(adminAcativityVo.getCode(), "验证码");
        ValidatorUtil.require(adminAcativityVo.getPhone(), "手机号");


        String userName = rsaService.RSADecrypt(adminAcativityVo.getUsername(),adminAcativityVo.getRandom());
        String phone = rsaService.RSADecrypt(adminAcativityVo.getPhone(),adminAcativityVo.getRandom());
        String code = rsaService.RSADecrypt(adminAcativityVo.getCode(),adminAcativityVo.getRandom());
        String pwd = Md5Utils.encode(rsaService.RSADecrypt(adminAcativityVo.getPassword(),adminAcativityVo.getRandom()));


        phoneValid(userName,phone,code);
//        phoneUnique(phone);

        QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
        queryWrapper.eq("username", userName);
        queryWrapper.eq("phone", phone);
        User user1 = userMapper.selectOne(queryWrapper);
        if (null == user1){
            throw new ValidatorException("账号和手机号不匹配");
        }

        User user = new User();
        user.setPassword(pwd);
//        user.setPhone(phone);
        user.setStatus(UserStatusEnum.normal.getIdentify());
        userMapper.update(user,queryWrapper);

        return null;
    }

    public void userNameUnique(String userName) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
        queryWrapper.eq("username", userName);
        User user = userMapper.selectOne(queryWrapper);
        if (null != user) {
            throw new ValidatorException("账号已经存在");
        }
    }

    public void phoneUnique(String phone) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
        queryWrapper.eq("phone", phone);
        User user = userMapper.selectOne(queryWrapper);
        if (null != user) {
            throw new ValidatorException("手机号已经存在");
        }
    }

    public User getUserByUserName(String userName) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
        queryWrapper.eq("username", userName);
        User user = userMapper.selectOne(queryWrapper);
        if (null == user) {
            throw new ValidatorException("账号不存在");
        }
        return user;
    }


    public void phoneValid(String userName,String phone,String code){
        if (!redisUtil.existKey(TokenUtils.getRedisKeyPhone(userName.concat(":").concat(phone)))){
            throw new ValidatorException("手机号:".concat(phone).concat("验证码不存在"));
        }

        String codeRedis = redisUtil.getValue(TokenUtils.getRedisKeyPhone(userName.concat(":").concat(phone)));

        if (!code.equals(codeRedis)){
            throw new ValidatorException("手机号:".concat(phone).concat("验证码错误"));
        }
    }

}
